interface QueueEntry {
	create?(): void;
	show(): void;
	hide(): void;
	destory?(): void;
}

class Instance<T extends QueueEntry> {
	closing = false;
	closeTimeout: number | null = null;
	opened: Promise<void>;
	constructor(
		private item: QueueItem<T>,
		private aniMs: number,
		onTimeout: () => void
	) {
		item.value.create?.();
		setTimeout(() => {
			item.value.show();
		}, 16);
		this.opened = new Promise((resolve) => {
			setTimeout(() => {
				resolve();
			}, aniMs);
		});
		if (typeof item.timeout === 'number' && item.timeout > 0) {
			this.closeTimeout = window.setTimeout(onTimeout, item.timeout);
		}
	}
	close(cb: (esc: boolean) => void, esc: boolean): void {
		if (this.closing) {
			return;
		}
		this.closing = true;
		if (this.closeTimeout !== null) {
			clearTimeout(this.closeTimeout);
		}
		this.opened.then(() => {
			this.item.value.hide();
			setTimeout(() => {
				this.item.value.destory?.();
				cb(esc);
			}, this.aniMs);
		});
	}
}
interface ActionQueue<T extends QueueEntry> {
	readonly top: T | null;
	show(value: T, timeout?: number): void;
	close(esc: boolean): void;
}

interface QueueItem<T extends QueueEntry> {
	value: T;
	timeout?: number;
}

export function actionQueue<T extends QueueEntry>(aniMs: number): ActionQueue<T> {
	const queue: QueueItem<T>[] = [];
	let instance: Instance<T> | null = null;
	function show(item: QueueItem<T>): void {
		instance = new Instance(item, aniMs, () => {
			close(false);
		});
		if (queue.length > 0) {
			close(false);
		}
	}
	function close(esc: boolean): void {
		if (instance && !instance.closing) {
			instance.close(() => {
				instance = null;
				const item = queue.shift();
				if (item) {
					show(item);
				}
			}, esc);
		}
	}
	return {
		show(value: T, timeout?: number) {
			if (instance) {
				queue.push({ value, timeout });
				close(false);
			} else {
				show({ value, timeout });
			}
		},
		close,
		get top() {
			return queue[0]?.value ?? null;
		},
	};
}
